package Semantic;

import Lexical.Tag;
import Lexical.Token;
import Lexical.Type;
import Lexical.Word;
import Semantic.SemanticTypes.CommandType;
import Semantic.SemanticTypes.OperandType;
import Syntactic.Environment;
import Util.FunctionsLibrary;
import Util.Stack;

public class SemanticAction 
{
	private Symbol currentSymbol = null;
	private Symbol subroutineCall = null;
	private Environment currentEnvironment = null;
	private CommandType currentCommand = null;
	private int subroutineParans = 0;
	
	// Stack for arithmetic operators and operands
	private Stack<Operand> operands = new Stack<Operand>();
	private Stack<Token> operators = new Stack<Token>();
	
	// Stack for relational operators and operands
	private Stack<Operand> relOperands = new Stack<Operand>();
	private Stack<Token> relOperators = new Stack<Token>();
	
	// Stack for logical operators and operands
	private Stack<Operand> logOperands = new Stack<Operand>();
	private Stack<Token> logOperators = new Stack<Token>();
	
	// Stack for the else labels
	private Stack<String> endifLabels = new Stack<String>();
	
	private Stack<String> loopLabels = new Stack<String>();
	private Stack<String> endLoopLabels = new Stack<String>();
	
	private CodeGenerator codeGen;
	
	// Counters
	private int labelCounter = 0;
	
	// Labels
	private final String LABEL_TEMP = "TEMP";
	private final String LABEL_BEGIN = "BEGIN";
	private final String LABEL_END = "END";
	private final String LABEL_VAR = "VAR";
	private final String LABEL_RELCOND = "REL";
	private final String LABEL_LOOP = "LOOP";
	private final String LABEL_ENDLOOP = "ENDLOOP";
	
	private FunctionsLibrary functions = new FunctionsLibrary();
	private boolean possibleArray = false;
	
	// ***********************************************************
	// Contructor
	// ***********************************************************
	public SemanticAction(String outputFile) 
	{
		// sets the root-Environvemnt
		this.currentEnvironment = new Environment("root");
		
		codeGen = new CodeGenerator(outputFile);
		codeGen.writeVariable("&", "/0000");
		codeGen.writeVariable("JP", LABEL_BEGIN);
		functions.writeIOFunctions(codeGen);
	}
	
	// ***********************************************************
	// Semantic Actions for the 'declaration' machine
	// ***********************************************************
	
	// Gets the symbol type (variables types, record object declaration or function/procedure)
	public void AS_DECLARATION_TYPE (Token tk)
	{
		if (currentSymbol == null)
		{
			currentSymbol = new Symbol();
			
			// VarType
			if (tk.getClass() == Type.class)
			{
				currentSymbol.type = SymbolType.fromString(tk.toString());
				currentCommand = CommandType.VAR_DECLARATION;
			}
			// RecordType
			else if (tk.tag == Tag.IDENTIFIER)
			{
				currentSymbol.type = SymbolType.RECORDVAR;
			}
			// function, procedure, record (declaration)
			else
			{
				currentSymbol.type = SymbolType.fromString(tk.toString());
			}
			
		}
		else
		{
			System.err.println("[AS_DECLARATION_TYPE] ERROR! There is already a symbol in use!");
			System.exit(0);
		}		
	}
	
	// Gets the symbol name and verifies the type previously received
	public void AS_DECLARATION_VARNAME (Token tk)
	{
		if (currentSymbol != null)
		{
			// first, see if there is already a variable with the same name
			if (this.currentEnvironment.getSymbol(tk.toString()) != null)
			{
				ERROR_DUPLICATEDSYMBOL (tk);
			}
			
			currentSymbol.name = tk.toString();
			
			// if function, procedure or program
			if ( 	this.currentSymbol.type == SymbolType.FUNCTION ) {
				currentSymbol.label = currentSymbol.name.toUpperCase();
				codeGen.setLabel(currentSymbol.label);
				codeGen.writeInstruction("JP","/000");
			}
				
			else if (	this.currentSymbol.type == SymbolType.PROCEDURE || 
						this.currentSymbol.type == SymbolType.PROGRAM 	||
						this.currentSymbol.type == SymbolType.RECORD	)
			{
				currentSymbol.label = currentSymbol.name.toUpperCase();
			}
			// if variable
			else
			{
				currentSymbol.label = LABEL_VAR + labelCounter;
				labelCounter++;
			}
		}
		else
		{
			System.err.println("[AS_DECLARATION_VARNAME] ERROR! No symbol declared!");
			System.exit(0);
		}
		
	}
	
	public void AS_DECLARATION_ARRAY (Token tk)
	{
		this.currentSymbol.dimension = Integer.parseInt(tk.toString());
	}
	
	// Declarations has ended; saves the symbol into the table
	public void AS_DECLARATION_END (Token tk)
	{
		
		if (currentCommand == CommandType.VAR_DECLARATION) {
			String name = this.currentSymbol.name;
			if (this.currentSymbol.dimension > 0){
				for (int i = 0; i < this.currentSymbol.dimension ;i ++) {
					Symbol s = new Symbol(name + "["+i+"]");
					s.label = LABEL_VAR + labelCounter;
					labelCounter++;
					this.currentEnvironment.addSymbol(s);
					codeGen.setLabel(s.label);
					codeGen.writeVariable("K", "/0");
				}
				
			} else {
				this.currentEnvironment.addSymbol(this.currentSymbol);
				codeGen.setLabel(currentSymbol.label);
				codeGen.writeVariable("K", "/0");
			}
		}
		
		this.currentSymbol = null;
	}
	
	// ***********************************************************
	// Semantic actions for the 'element' machine
	// ***********************************************************
	
	// Creates a new scope, that is pointed by a "symbol-pointer"
	// for (funcions, procedures, programs) declarations, "is" opens the scope
	// for (record), "identifier" (i.e., record-name) opens the scope ( TODO: this should be corrected in a future version - add "is")
	public void AS_ELEMENT_NEWSCOPE (Token tk)
	{
		if (tk.tag == Tag.IDENTIFIER 	&&
				(	this.currentSymbol.type == SymbolType.FUNCTION 	||
					this.currentSymbol.type == SymbolType.PROCEDURE	||
					this.currentSymbol.type == SymbolType.RECORD 	||
					this.currentSymbol.type == SymbolType.PROGRAM	))
		{
			this.AS_DECLARATION_VARNAME(tk);
		}
		
		// creates a new scope
		this.currentSymbol.env = new Environment(this.currentSymbol.name, this.currentEnvironment);
		// add symbol to the table
		this.currentEnvironment.addSymbol(this.currentSymbol);
		// change scope		
		this.currentEnvironment = this.currentSymbol.env;		
		// clear current symbol
		this.currentSymbol = null;
		
	}
	
	// Closes a scopes and goes back to the previous one
	public void AS_ELEMENT_ENDSCOPE (Token tk)
	{
		if (this.currentEnvironment.previous != null)
		{
			// first, see if it's a PROGRAM scope. If so, then it's the end of the code
			if (this.currentEnvironment.getSymbol(this.currentEnvironment.getEnvironmentName()).type == SymbolType.PROGRAM)
			{
				codeGen.setLabel(LABEL_END);
				codeGen.writeInstruction("HM", "/00");
				codeGen.writeInstruction("#", LABEL_END);
				codeGen.generateCodeFile();
			}
			if (this.currentEnvironment.getSymbol(this.currentEnvironment.getEnvironmentName()).type == SymbolType.FUNCTION)
			{
				codeGen.writeInstruction("RS", this.currentEnvironment.getSymbol(this.currentEnvironment.getEnvironmentName()).label);
			}
			
			// go to the parent-environment
			this.currentEnvironment = this.currentEnvironment.previous;
			
		}
		else
		{
			// hum... this shouldn't be happening... =(
		}
		
	}
	
	// Sets the label to the code
	public void AS_ELEMENT_BEGINPROGRAM (Token tk) {
		codeGen.setLabel(LABEL_BEGIN);
	}
	
	public void AS_FUNCPROC_PARAMSDECLARATION (Token tk)
	{
		// this symbol represents the number of parameters the function has
		Symbol params = new Symbol(SymbolType.PARAMS.getType());
		params.type = SymbolType.PARAMS;
		params.dimension = 0; // the dimension represents the # of parameters
		this.currentEnvironment.addSymbol(params);
	}
	
	public void AS_FUNCPROC_ENDPARAMSDECLARATION (Token tk)
	{
		// finalizes last parameter (the others were finalized by ",")
		this.AS_DECLARATION_END(tk);
		
		// updates the # of parameters
		Symbol params = new Symbol(SymbolType.PARAMS.getType());
		params.type = SymbolType.PARAMS;
		params.dimension = this.currentEnvironment.symbolTable.getLength() - 1; // the dimension represents the # of parameters
		this.currentEnvironment.updateSymbol(params);
		
	}
	
	// ***********************************************************
	// Semantic actions for the 'expression' machine
	// ***********************************************************
	
	public void AS_EXPRESSION_ADDOPERAND (Token tk)
	{
		if (tk.tag == Tag.NUMBER)
			operands.push(new Operand(tk.toString(), OperandType.LITERAL));
		
		else if (tk.tag == Tag.IDENTIFIER) 
		{
			Symbol symbol = this.currentEnvironment.getSymbol(tk.toString());
			if (symbol == null){
				currentSymbol.name = tk.toString();
				possibleArray = true;
				//ERROR_UNDECLAREDSYMBOL(tk);
			}
			else if (symbol.type == SymbolType.FUNCTION){
				subroutineCall = symbol;
			}
			else {
				operands.push(new Operand(symbol.label, OperandType.VAR));
			}
			
		}
	}
	
	public void AS_EXPRESSION_ARRAY(Token tk) {
		possibleArray = false;
		Token t = new Word(Tag.IDENTIFIER, currentSymbol.name + "["+tk.toString()+"]");
		AS_EXPRESSION_ADDOPERAND(t);
	}
	
	public void AS_EXPRESSION_ADDOPERATOR (Token tk)
	{
		// There is only one operand provided until now
		if (operands.getLength() <= 1) 
		{
			operators.push(tk);
		} 
		else 
		{
			while (evalArithmeticPrecedence(tk)) 
			{
				executeArithmeticOperation();
			}
			
			operators.push(tk);
		}
	}
	
	public void AS_EXPRESSION_ADDRELOPERATOR (Token tk) {
		// Left side of the expression
		if (relOperands.isEmpty()) {
			while (!operators.isEmpty()) {
				executeArithmeticOperation();
			}
			
			// Gets the last operand into the stack and copies it to the variable
			String operator = (operands.getFirst().type == OperandType.LITERAL) ? "LV" : "LD";
			String operand = ((operands.getFirst().type == OperandType.LITERAL) ? "=" + operands.pop().value : operands.pop().value);
			codeGen.writeInstruction(operator, operand);
			
			String relOperand = getTempLabel(); 
			codeGen.writeInstruction("MM", relOperand);
			operands.pop();
			
			relOperands.push(new Operand(relOperand, OperandType.VAR));
			relOperators.push(tk);
		}
	}
	
	public void AS_EXPRESSION_SUBROUTINECALL (Token tk) {
		if (tk.toString().equals("(")) {
			subroutineParans = subroutineCall.env.getSymbol(SymbolType.PARAMS.getType()).dimension;
		} 
		else if (tk.toString().equals(",") || tk.toString().equals(")")) {
			if (subroutineParans > 0) {
				int index = subroutineCall.env.symbolTable.getNodeByIndex(0).dimension - subroutineParans;
				subroutineParans--;
				Symbol param =  subroutineCall.env.symbolTable.getNodeByIndex(index + 1);
				
				while (!operators.isEmpty()) {
					executeArithmeticOperation();
				}
				
				String operator = (operands.getFirst().type == OperandType.LITERAL) ? "LV" : "LD";
				String operand = ((operands.getFirst().type == OperandType.LITERAL) ? "=" + operands.pop().value : operands.pop().value);
				codeGen.writeInstruction(operator, operand);
				codeGen.writeInstruction("MM", param.label);
				
				if (tk.toString().equals(")")) {
					if (subroutineParans > 0) {
						ERROR_FUNCTIONPARAMSNOTSPECIFIED(tk);
					} else {
						codeGen.writeInstruction("SC", subroutineCall.label);
						if (subroutineCall.type == SymbolType.FUNCTION) {
							int returnSymbolIndex = subroutineCall.env.symbolTable.getLength();
							Symbol returnSymbol = subroutineCall.env.symbolTable.getNodeByIndex(returnSymbolIndex - 1);
							operands.push(new Operand(returnSymbol.label, OperandType.VAR));
						}
						subroutineCall = null;
					}
				}
			} 
			else { 
				ERROR_FUNCTIONPARAMSOVERPROVIDED(tk);
			}
		}
	}
	
	// ***********************************************************
	// Semantic actions for the 'statement' machine	
	// ***********************************************************
	
	public void AS_STATEMENT_VARIABLE (Token tk) {
		this.currentSymbol = this.currentEnvironment.getSymbol(tk.toString());
	}
	
	public void AS_STATEMENT_ARRAY_BEGIN (Token tk){
		possibleArray = false;
		Token t = new Word(Tag.IDENTIFIER, currentSymbol.name + "["+tk.toString()+"]");
		AS_STATEMENT_BEGIN (t);
	}
	
	public void AS_STATEMENT_BEGIN (Token tk)
	{
		if (tk.tag == Tag.IDENTIFIER) 
		{
			this.currentSymbol = this.currentEnvironment.getSymbol(tk.toString());
			
			if (this.currentSymbol == null)
			{
				possibleArray = true;
				currentSymbol = new Symbol();
				currentSymbol.name = tk.toString();
//				System.out.println("     >> " + tk);
//				this.ERROR_UNDECLAREDSYMBOL(tk);
			}
			
		}
		else if (tk.tag == Tag.IF) 
		{
			currentCommand = CommandType.CONDITIONAL;
		}
		else if (tk.tag == Tag.WHILE) 
		{
			currentCommand = CommandType.LOOP;
			codeGen.setLabel(LABEL_LOOP + labelCounter);
			loopLabels.push(LABEL_LOOP + labelCounter);
		}
		else if (tk.tag == Tag.INPUT) 
		{
			currentCommand = CommandType.INPUT;
		}
		else if (tk.tag == Tag.OUTPUT) {
			currentCommand = CommandType.OUTPUT;
		}		
	}
	
	public void AS_STATEMENT_ASSIGN (Token tk)
	{
		currentCommand = CommandType.ASSIGN;
	}
	
	public void AS_STATEMENT_END (Token tk) {
		if (possibleArray) {
			System.err.println("\nvariable not declared line " + tk.line);
			System.exit(0);
		}
		if (currentCommand == CommandType.INPUT){
			codeGen.writeInstruction("SC", "MVN_READ");
			codeGen.writeInstruction("MM", currentSymbol.label);
		}
		
		if (currentCommand == CommandType.OUTPUT){
			codeGen.writeInstruction("LD", currentSymbol.label);
			codeGen.writeInstruction("SC", "MVN_PRINT");
		}
		
		if (currentSymbol != null) {
			if (currentCommand == CommandType.ASSIGN && currentSymbol.type == SymbolType.INTEGER && tk.toString().equals(";")) {
				// while there is operators in the stack, execute the operation.
				while (!operators.isEmpty()) {
					executeArithmeticOperation();
				}
				
				// Gets the last operand into the stack and copies it to the variable
				String operator = (operands.getFirst().type == OperandType.LITERAL) ? "LV" : "LD";
				String operand = ((operands.getFirst().type == OperandType.LITERAL) ? "=" + operands.pop().value : operands.pop().value);
				codeGen.writeInstruction(operator, operand);
				codeGen.writeInstruction("MM", currentSymbol.label);
			}
			else if (currentCommand == CommandType.ASSIGN && currentSymbol.type == SymbolType.BOOLEAN && tk.toString().equals(";")) {
				// while there is operators in the stack, execute the operation.
				while (!operators.isEmpty()) {
					executeArithmeticOperation();
				}
				
				if (!operands.isEmpty()) {
					relOperands.push(operands.pop());
					executeRelationalOperation();
				}
				
				if (!logOperators.isEmpty()) {
					// TODO implementar AND OR NOT
				}
				
				// Gets the results in pilha
				String operator = (logOperands.getFirst().type == OperandType.LITERAL) ? "LV" : "LD";
				String operand = ((logOperands.getFirst().type == OperandType.LITERAL) ? "=" + logOperands.pop().value : logOperands.pop().value);
				codeGen.writeInstruction(operator, operand);
				codeGen.writeInstruction("MM", currentSymbol.label);
			}
		}
		else if (tk.tag == Tag.THEN) {
			// while there is operators in the stack, execute the operation.
			while (!operators.isEmpty()) {
				executeArithmeticOperation();
			}
			
			if (!operands.isEmpty()) {
				relOperands.push(operands.pop());
				executeRelationalOperation();
			}
			
			if (!logOperators.isEmpty()) {
				// TODO
			}
			
			String operator = (logOperands.getFirst().type == OperandType.LITERAL) ? "LV" : "LD";
			String operand = ((logOperands.getFirst().type == OperandType.LITERAL) ? "=" + logOperands.pop().value : logOperands.pop().value);
			codeGen.writeInstruction(operator, operand);
			codeGen.writeInstruction("JZ", LABEL_END + labelCounter);
			endifLabels.push(LABEL_END + labelCounter);
			labelCounter++;
		} 
		else if (tk.tag == Tag.ENDIF) {
			codeGen.setLabel(endifLabels.pop());
		}
		else if (tk.tag == Tag.LOOP) {
			// while there is operators in the stack, execute the operation.
			while (!operators.isEmpty()) {
				executeArithmeticOperation();
			}
			
			if (!operands.isEmpty()) {
				relOperands.push(operands.pop());
				executeRelationalOperation();
			}
			
			if (!logOperators.isEmpty()) {
				// TODO
			}
			
			String operator = (logOperands.getFirst().type == OperandType.LITERAL) ? "LV" : "LD";
			String operand = ((logOperands.getFirst().type == OperandType.LITERAL) ? "=" + logOperands.pop().value : logOperands.pop().value);
			
			codeGen.writeInstruction(operator, operand);
			codeGen.writeInstruction("JZ", LABEL_ENDLOOP + labelCounter);
			endLoopLabels.push(LABEL_ENDLOOP + labelCounter);
			labelCounter++;
		}
		else if (tk.tag == Tag.ENDLOOP) {
			codeGen.writeInstruction("JP", loopLabels.pop());
			codeGen.setLabel(endLoopLabels.pop());
		}
		
		// clear current symbol for use
		currentSymbol = null;
		currentCommand = null;
		operands = new Stack<Operand>();
		operators = new Stack<Token>();
	}
	
	// ***********************************************************
	// Other methods
	// ***********************************************************
	
	private String getTempLabel() {
		String temp = LABEL_TEMP + labelCounter;
		labelCounter++;
		
		codeGen.setLabel(temp);
		codeGen.writeVariable("K", "/0");
		
		return temp;
	}
	
	private void executeArithmeticOperation() {		
		// Gets the operator from the stack
		String operator = operators.pop().toString();
		
		// Gets the operands from the stack
		Operand operand2 = operands.pop();
		Operand operand1 = operands.pop();
		
		// Defines which mvn load operator to use - LV or LD - based on the operand type
		String loadOperator = (operand1.type == OperandType.LITERAL) ? "LV" : "LD";
		
		// Gets the operand1 string
		String strOperand1 = (operand1.type == OperandType.LITERAL) ? "=" + operand1.value : operand1.value;
		
		// Gets the operand2 string
		String strOperand2;
		if (operand2.type == OperandType.LITERAL) {
			// If the operand2 is a literal number, it has to be loaded into another temp variable
			codeGen.writeInstruction("LV", "=" + operand2.value);
			strOperand2 = getTempLabel();
			codeGen.writeInstruction("MM", strOperand2 );
		} else {
			strOperand2 = operand2.value;
		}
		
		// Generate the code for the operation
		String result = getTempLabel();
		codeGen.writeInstruction(loadOperator, strOperand1);
		codeGen.writeInstruction(operator, strOperand2);
		codeGen.writeInstruction("MM", result);
		
		// Pushes the temporary variable into the stack
		operands.push(new Operand(result, OperandType.VAR));
	}
	
	private void executeRelationalOperation() {
		// Gets the operator from the stack
		String operator = relOperators.pop().toString();
		
		// Gets the operands from the stack
		Operand operand1, operand2;
		
		if (operator.equals("<") || operator.equals("<=")) {
			operand1 = relOperands.pop();
			operand2 = relOperands.pop();
		} else {
			operand2 = relOperands.pop();
			operand1 = relOperands.pop();
		}
		
		// Defines which mvn load operator to use - LV or LD - based on the operand type
		String loadOperator = (operand1.type == OperandType.LITERAL) ? "LV" : "LD";
		
		// Gets the operand1 string
		String strOperand1 = (operand1.type == OperandType.LITERAL) ? "=" + operand1.value : operand1.value;
		
		// Gets the operand2 string
		String strOperand2;
		if (operand2.type == OperandType.LITERAL) {
			// If the operand2 is a literal number, it has to be loaded into another temp variable
			codeGen.writeInstruction("LV", "=" + operand2.value);
			strOperand2 = getTempLabel();
			codeGen.writeInstruction("MM", strOperand2 );
		} else {
			strOperand2 = operand2.value;
		}
		
		// Generate the code for the operation
		codeGen.writeInstruction(loadOperator, strOperand1);
		codeGen.writeInstruction("-", strOperand2);
		
		String result = getTempLabel();
		
		String relLabel = LABEL_RELCOND + labelCounter;
		labelCounter++;
		
		String endLabel = LABEL_END + labelCounter;
		labelCounter++;
		
		if (operator.equals(">") || operator.equals("<")) {
			codeGen.writeInstruction("JN", relLabel);
			codeGen.writeInstruction("JZ", relLabel);
			codeGen.writeInstruction("LV", "=1");
			codeGen.writeInstruction("JP", endLabel);
			codeGen.setLabel(relLabel);
			codeGen.writeInstruction("LV", "=0");
			codeGen.setLabel(endLabel);
			codeGen.writeInstruction("MM", result);
		}
		else if (operator.equals(">=") || operator.equals("<=")) {
			codeGen.writeInstruction("JN", relLabel);
			codeGen.writeInstruction("LV", "=1");
			codeGen.writeInstruction("JP", endLabel);
			codeGen.setLabel(relLabel);
			codeGen.writeInstruction("LV", "=0");
			codeGen.setLabel(endLabel);
			codeGen.writeInstruction("MM", result);
		}
		else if (operator.equals("==")) {
			codeGen.writeInstruction("JZ", relLabel);
			codeGen.writeInstruction("LV", "=0");
			codeGen.writeInstruction("JP", endLabel);
			codeGen.setLabel(relLabel);
			codeGen.writeInstruction("LV", "=1");
			codeGen.setLabel(endLabel);
			codeGen.writeInstruction("MM", result);
		}
		else if (operator.equals("!=")) {
			codeGen.writeInstruction("JZ", relLabel);
			codeGen.writeInstruction("LV", "=1");
			codeGen.writeInstruction("JP", endLabel);
			codeGen.setLabel(relLabel);
			codeGen.writeInstruction("LV", "=0");
			codeGen.setLabel(endLabel);
			codeGen.writeInstruction("MM", result);
		}
		
		logOperands.push(new Operand(result, OperandType.VAR));
	}
	
	private boolean evalArithmeticPrecedence (Token tk) {
		boolean precedence = false;
		
		if (operators.getFirst().toString().equals("/")) {
			precedence = true;
		} 
		else if (operators.getFirst().toString().equals("*")) {
			if (tk.toString().equals("+") || tk.toString().equals("-"))
				precedence = true;
			else if (tk.toString().equals("*") || tk.toString().equals("/"))
				precedence = false;
		}
		else if (operators.getFirst().toString().equals("-")) {
			if (tk.toString().equals("+"))
				precedence = true;
			else if (tk.toString().equals("*") || tk.toString().equals("/") || tk.toString().equals("-"))
				precedence = false;
		}
		else if (operators.getFirst().toString().equals("+")) {
				precedence = false;
		}
		
		return precedence;
	}
	
	// *********************
	// ERROR MESSAGES
	// *********************
	
	private void ERROR_UNDECLAREDSYMBOL(Token tk)
	{
		System.err.println("*****************************************************************");
		System.err.println("Semantic Error! (line " + tk.line + "): \'" + tk.toString() + "\' not declared!");
		System.exit(0);
	}
	
	private void ERROR_DUPLICATEDSYMBOL(Token tk)
	{
		System.err.println("*****************************************************************");
		System.err.println("Semantic Error! (line " + tk.line + "): \'" + tk.toString() + "\' already exists!");
		System.exit(0);
	}
	
	private void ERROR_FUNCTIONPARAMSNOTSPECIFIED(Token tk)
	{
		System.err.println("*****************************************************************");
		System.err.println("Semantic Error! (line " + tk.line + "): \'" + "Some params were not specified for the subroutine " + subroutineCall.name);
		System.exit(0);
	}
	
	private void ERROR_FUNCTIONPARAMSOVERPROVIDED(Token tk)
	{
		System.err.println("*****************************************************************");
		System.err.println("Semantic Error! (line " + tk.line + "): \'" + "The subroutine " + subroutineCall.name + 
						   " has only " + subroutineCall.env.symbolTable.getNodeByIndex(0) + " parameters");
		System.exit(0);
	}
	
	private void ERROR_PARAMTYPEERROR(Token tk)
	{
		System.err.println("*****************************************************************");
		System.err.println("Semantic Error! (line " + tk.line + "): \'" + "One param received does not reflect the expected type");
		System.exit(0);
	}
}

